package io.cucumber.core.runner;

import com.zws.cucumber.ZwsUtils;
import com.zws.cucumber.snapshot.ZwsSnapshotFacade;
import com.zws.cucumber.spring.configuration.ZwsCommonConf;
import com.zws.cucumber.util.colorfullog.ZwsColorfulLogger;
import io.cucumber.core.gherkin.Pickle;
import io.cucumber.core.gherkin.messages.CoreGherkinMessagePickleHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Iterator;
import java.util.List;

public class CoreRunnerSnapshotTestCaseHelper {

    private static final Logger logger = ZwsColorfulLogger.of(LoggerFactory.getLogger(CoreRunnerSnapshotTestCaseHelper.class));

    private static final String SNAPSHOT_BACKGROUND = "background";

    private static volatile ZwsSnapshotFacade snapshotFacade;

    private static String skipStepsText;

    private static ZwsSnapshotFacade getSnapshotFacade() {
        if (snapshotFacade == null) {
            synchronized (ZwsSnapshotFacade.class) {
                if (snapshotFacade == null) {
                    snapshotFacade = (ZwsSnapshotFacade) ZwsCommonConf.getApplicationContext().getBean("snapshotFacade");
                }
            }
        }
        return snapshotFacade;
    }

    public static String getFeaturePath(io.cucumber.plugin.event.TestCase eventTestCase) {
        TestCase testCase = (TestCase) eventTestCase;
        Pickle pickle = getFieldValue(testCase, "pickle");
        return pickle.getUri().toString();
    }

    public static boolean injectSnapshotTakeStep(io.cucumber.plugin.event.TestCase eventTestCase) {

        TestCase testCase = (TestCase) eventTestCase;
        int lineOfScenario = getScenarioLine(testCase);

        List<PickleStepTestStep> testSteps = getFieldValue(testCase, "testSteps");
        StringBuilder sb = new StringBuilder();
        int idx = 0;
        for (; idx < testSteps.size(); idx++) {
            PickleStepTestStep step = testSteps.get(idx);
            if (step.getStepLine() > lineOfScenario) {
                break;
            }
            sb.append("\t").append(step.getStepText()).append("\n");
        }
        if (idx > 0) {
            logger.debug("add snapshot take step at {}", idx);
//            getSnapshotFacade().deleteSnapshot("test");
            testSteps.add(0, CoreRunnerSnapshotTestStepHelper.newSnapshotDeletePickleStepTestStep(CoreRunnerSnapshotTestCaseHelper::getSnapshotFacade, SNAPSHOT_BACKGROUND));
            testSteps.add(idx + 1, CoreRunnerSnapshotTestStepHelper.newSnapshotTakePickleStepTestStep(CoreRunnerSnapshotTestCaseHelper::getSnapshotFacade, SNAPSHOT_BACKGROUND));
            skipStepsText = sb.toString();
            return true;
        }
        return false;
    }

    public static void skipBackgroundStepAndInjectSnapshotRestoreStep(io.cucumber.plugin.event.TestCase eventTestCase) {
        TestCase testCase = (TestCase) eventTestCase;
        int lineOfScenario = getScenarioLine(testCase);

        List<PickleStepTestStep> testSteps = getFieldValue(testCase, "testSteps");
        int originalSize = testSteps.size();
        Iterator<PickleStepTestStep> itor = testSteps.iterator();
        while (itor.hasNext()) {
            PickleStepTestStep step = itor.next();
            if (step.getStepLine() < lineOfScenario) {
                itor.remove();
            } else {
                break;
            }
        }
        logger.debug("skip background step size: {}", originalSize - testSteps.size());
        testSteps.add(0, CoreRunnerSnapshotTestStepHelper.newSnapshotRestorePickleStepTestStep(CoreRunnerSnapshotTestCaseHelper::getSnapshotFacade, SNAPSHOT_BACKGROUND, skipStepsText));
    }

    private static int getScenarioLine(TestCase testCase) {
        return CoreGherkinMessagePickleHelper.getScenarioLine(getFieldValue(testCase, "pickle"));
    }

    private static <T> T getFieldValue(Object object, String fieldName) {
        return ZwsUtils.getFieldValue(TestCase.class, fieldName, object);
    }

    private static void setFieldValue(Object object, String fieldName, Object value) {
        ZwsUtils.setFieldValue(TestCase.class, fieldName, object, value);
    }

}
